package com.sunxiansheng.log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Aspect
@Slf4j
@Component
@ConditionalOnProperty(name = "log.aspect.enable", havingValue = "true", matchIfMissing = true)
public class LogAspect {

    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

    private static final String ANSI_RESET = "\u001B[0m";
    private static final String ANSI_BLUE = "\u001B[34m";
    private static final String ANSI_YELLOW = "\u001B[33m";
    private static final String ANSI_GREEN = "\u001B[32m";
    private static final String ANSI_PURPLE = "\u001B[35m";

    /**
     * 配置切点，切Controller和Service的所有方法
     */
    @Pointcut("execution(* com.sunxiansheng.*.controller.*Controller.*(..)) || execution(* com.sunxiansheng.*.service.*Service.*(..))")
    private void pointCut() {}

    /**
     * 环绕通知
     * @param pjp
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 获取参数
        Object[] args = pjp.getArgs();
        // 序列化为json字符串
        String req = GSON.toJson(args);
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取类名+方法名
        String className = methodSignature.getDeclaringType().getName();
        String methodName = methodSignature.getName();

        // 获取线程信息和时间戳
        String threadName = Thread.currentThread().getName();
        long threadId = Thread.currentThread().getId();
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        // 打印：方法名，请求参数，线程信息，时间戳
        log.info("\n========================\n" +
                        "时间戳：{}{}{}\n" +
                        "线程：{}{} (ID: {}){}\n" +
                        "方法名：{}{}{}\n" +
                        "请求参数：{}{}{}\n" +
                        "========================",
                ANSI_GREEN, timestamp, ANSI_RESET,
                ANSI_PURPLE, threadName, threadId, ANSI_RESET,
                ANSI_BLUE, className + "." + methodName, ANSI_RESET,
                ANSI_YELLOW, req, ANSI_RESET);

        long startTime = System.currentTimeMillis();
        Object proceed;
        try {
            // 执行目标方法
            proceed = pjp.proceed();
        } catch (Throwable throwable) {
            // 捕获并记录异常
            log.error("\n========================\n" +
                            "时间戳：{}{}{}\n" +
                            "线程：{}{} (ID: {}){}\n" +
                            "方法名：{}{}{}\n" +
                            "执行异常：{}{}{}\n" +
                            "堆栈信息：{}{}\n" +
                            "========================",
                    ANSI_GREEN, timestamp, ANSI_RESET,
                    ANSI_PURPLE, threadName, threadId, ANSI_RESET,
                    ANSI_BLUE, className + "." + methodName, ANSI_RESET,
                    ANSI_YELLOW, throwable.getMessage(), ANSI_RESET, throwable,
                    ANSI_RESET);
            throw throwable;  // 重新抛出异常
        }
        // 拿到出参
        String resp = GSON.toJson(proceed).replace("\n", "\n  ");
        long endTime = System.currentTimeMillis();
        // 打印响应参数和耗时
        log.info("\n========================\n" +
                        "时间戳：{}{}{}\n" +
                        "线程：{}{} (ID: {}){}\n" +
                        "方法名：{}{}{}\n" +
                        "响应参数：{}{}{}\n" +
                        "耗时：{}{}{} ms\n" +
                        "========================",
                ANSI_GREEN, timestamp, ANSI_RESET,
                ANSI_PURPLE, threadName, threadId, ANSI_RESET,
                ANSI_BLUE, className + "." + methodName, ANSI_RESET,
                ANSI_YELLOW, "\n  " + resp, ANSI_RESET,
                ANSI_GREEN, endTime - startTime, ANSI_RESET);
        // 返回目标方法的执行结果
        return proceed;
    }
}